#ifndef ORG_WXW0303_COMMON_SCOPEGUARD_H
#define ORG_WXW0303_COMMON_SCOPEGUARD_H

#include <utility>

#include "preprocess.h"

template <typename F>
class ScopeGuard {
public:
    explicit ScopeGuard(const F &f);
    explicit ScopeGuard(F &&f);
    ScopeGuard(ScopeGuard &&sc);
    ~ScopeGuard() noexcept;

    ScopeGuard& operator=(ScopeGuard &&sc);

    void setDismiss(bool arg = true) noexcept;
private:
    bool dismiss_ = false;
    F f_;

    DENY_COPY(ScopeGuard);
    DENY_ASSIGN(ScopeGuard);
};

template <typename F>
inline ScopeGuard<F>::ScopeGuard(const F &f):
    f_(f)
{
}

template <typename F>
inline auto ScopeGuard<F>::operator=(ScopeGuard &&sc) -> ScopeGuard&
{
    dismiss_ = sc.dismiss_;
    f_ = std::move(sc.f_);
    sc.dismiss_ = true;
    return *this;
}

template <typename F>
inline ScopeGuard<F>::ScopeGuard(F &&f):
    f_(std::move(f))
{
}

template <typename F>
inline ScopeGuard<F>::ScopeGuard(ScopeGuard &&sc):
    dismiss_(sc.dismiss_),
    f_(std::move(sc.f_))
{
    sc.dismiss_ = true;
    return ;
}

template <typename F>
inline ScopeGuard<F>::~ScopeGuard() noexcept
{
    if (!dismiss_)
        f_();
    return ;
}

template <typename F>
inline void ScopeGuard<F>::setDismiss(bool arg) noexcept
{
    dismiss_ = arg;
    return ;
}

template <typename F>
inline ScopeGuard<typename std::decay<F>::type>
makeGuard(F &&fn)
{
    return ScopeGuard<typename std::decay<F>::type>(std::forward<F>(fn));
}

namespace ww {
namespace detail {

enum class ScopeGuardOnExit {};

template <typename F>
inline ScopeGuard<typename std::decay<F>::type>
operator*(ScopeGuardOnExit, F &&fn)
{
    return makeGuard(std::forward<F>(fn));
}


} // namespace ww::detail
} // namespace ww

#define ON_SCOPE_EXIT \
    auto ANONYMOUS_NAME(SCOPE_EXIT_OBJ)  = ::ww::detail::ScopeGuardOnExit() * \
    [&]() noexcept -> void

#endif // ORG_WXW0303_COMMON_SCOPEGUARD_H
